package com.hex.ds.hdtp.core.app.script.analyze.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.hex.ds.hdtp.core.app.common.enums.BasicTaskStatus;
import com.hex.ds.hdtp.core.app.common.page.PageData;
import com.hex.ds.hdtp.core.app.common.page.converter.IPageAndPageDtoConverter;
import com.hex.ds.hdtp.core.app.common.page.dto.PageDto;
import com.hex.ds.hdtp.core.app.data.table.dto.request.TableTransferListRequestDto;
import com.hex.ds.hdtp.core.app.data.table.dto.request.TableTransferTaskRequestDto;
import com.hex.ds.hdtp.core.app.data.table.service.ITableTransferListService;
import com.hex.ds.hdtp.core.app.data.table.service.ITableTransferTaskService;
import com.hex.ds.hdtp.core.app.script.analyze.converter.IScriptAnalyzeTaskConverter;
import com.hex.ds.hdtp.core.app.script.analyze.dto.request.ScriptAnalyzeListRequestDto;
import com.hex.ds.hdtp.core.app.script.analyze.dto.request.ScriptAnalyzeTaskRequestDto;
import com.hex.ds.hdtp.core.app.script.analyze.dto.response.ScriptAnalyzeListResponseDto;
import com.hex.ds.hdtp.core.app.script.analyze.dto.response.ScriptAnalyzeTaskResponseDto;
import com.hex.ds.hdtp.core.app.script.analyze.dto.response.ScriptAnalyzeTransferResponseDto;
import com.hex.ds.hdtp.core.app.script.analyze.handler.IScriptAnalyzeHandlerService;
import com.hex.ds.hdtp.core.app.script.analyze.service.IScriptAnalyzeListService;
import com.hex.ds.hdtp.core.app.script.analyze.service.IScriptAnalyzeTaskService;
import com.hex.ds.hdtp.core.app.script.analyze.service.IScriptAnalyzeTransferService;
import com.hex.ds.hdtp.core.app.script.common.dto.response.ScriptTableConsanguinityResponseDto;
import com.hex.ds.hdtp.core.app.script.common.service.IScriptTableConsanguinityService;
import com.hex.ds.hdtp.core.app.script.common.service.impl.ScriptConvertFileFilter;
import com.hex.ds.hdtp.core.inf.common.page.Page;
import com.hex.ds.hdtp.core.inf.script.analyze.po.ScriptAnalyzeTaskPo;
import com.hex.ds.hdtp.core.inf.script.analyze.repository.IScriptAnalyzeTaskRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.charset.Charset;
import java.util.List;

import static com.hex.ds.hdtp.core.app.common.enums.BasicTaskStatus.FINISH;
import static com.hex.ds.hdtp.core.app.common.enums.TransferTaskType.*;

/**
 * @Package com.hex.ds.hdtp.core.app.script.analyze.service.impl
 * @ClassName ScriptAnalyzeTaskService
 * @Description 脚本分析任务-业务接口实现
 * @Author gj.xu
 * @Date 2023/12/7 15:06
 * @Version v2.0
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy, @Autowired})
public class ScriptAnalyzeTaskService implements IScriptAnalyzeTaskService {

    private final IScriptAnalyzeTaskConverter converter;

    private final IPageAndPageDtoConverter pageAndPageDtoConverter;

    private final IScriptAnalyzeTaskRepository scriptAnalyzeTaskRepository;

    private final IScriptAnalyzeListService scriptAnalyzeListService;

    private final IScriptAnalyzeTransferService scriptAnalyzeTransferService;

    private final IScriptAnalyzeHandlerService scriptAnalyzeService;

    private final IScriptTableConsanguinityService scriptTableConsanguinityService;

    private final ITableTransferTaskService tableTransferTaskService;

    private final ITableTransferListService tableTransferListService;

    private final ScriptConvertFileFilter scriptFileFilter;

    @Value("${script.analyze.relativePath}")
    private String relativePath;

    /**
     * @Method queryListByPage
     * @Param
     * @Param scriptAnalyzeTaskRequestDto
     * @Param pageDto
     * @Return com.hex.ds.hdtp.core.app.common.page.PageData
     * @Description 分页查询
     * @Author gj.xu
     * @Date 2023/12/7 15:30
     * @Version V2.0
     */
    @Override
    public PageData queryListByPage(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto, PageDto pageDto) {
        final Page<ScriptAnalyzeTaskPo> pageResult = scriptAnalyzeTaskRepository.queryListByPage(
                converter.toScriptAnalyzeTaskPo(scriptAnalyzeTaskRequestDto), pageAndPageDtoConverter.toPage(pageDto));
        List<ScriptAnalyzeTaskResponseDto> responseDtos = converter.toScriptAnalyzeTaskResponseDtoList(pageResult.getData());
        getTransferTask(responseDtos);
        return new PageData(responseDtos, pageResult.getTotal());
    }

    /**
     * @Method getTransferTask
     * @Param responseDtos
     * @Return java.util.List<com.hex.ds.hdtp.core.app.script.analyze.dto.response.ScriptAnalyzeTaskResponseDto>
     * @Description 填充各迁移模块任务对象
     * @Author gj.xu
     * @Date 2023/12/12 16:15
     * @Version V2.0
     */
    private List<ScriptAnalyzeTaskResponseDto> getTransferTask(List<ScriptAnalyzeTaskResponseDto> responseDtos) {
        for (ScriptAnalyzeTaskResponseDto scriptAnalyzeTask : responseDtos) {
            List<ScriptAnalyzeTransferResponseDto> transferTask = scriptAnalyzeTransferService.queryListByAnalyzeTaskId(scriptAnalyzeTask.getPkId());
            scriptAnalyzeTask.setTransferTask(transferTask);
        }
        return responseDtos;
    }

    /**
     * @Method queryById
     * @Param
     * @Param pkId
     * @Return com.hex.ds.hdtp.core.app.script.analyze.dto.response.ScriptAnalyzeTaskResponseDto
     * @Description 根据主键查询
     * @Author gj.xu
     * @Date 2023/12/8 11:37
     * @Version V2.0
     */
    @Override
    public ScriptAnalyzeTaskResponseDto queryById(String pkId) {
        return converter.toScriptAnalyzeTaskResponseDto(scriptAnalyzeTaskRepository.queryById(pkId));
    }

    /**
     * @Method createTask
     * @Param
     * @Param scriptAnalyzeTaskRequestDto
     * @Return java.lang.String
     * @Description 新增脚本分析任务及脚本清单
     * @Author gj.xu
     * @Date 2023/12/8 13:52
     * @Version V2.0
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String createTask(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        ScriptAnalyzeTaskPo scriptAnalyzeTaskPo = addScriptAnalyzeTask(scriptAnalyzeTaskRequestDto);
        // TODO 当前为用户上传脚本，后可以支持从数据库中选择脚本
        addScriptAnalyzeList(scriptAnalyzeTaskPo, scriptAnalyzeTaskRequestDto);
        scriptAnalyze(scriptAnalyzeTaskPo.getPkId());
        return scriptAnalyzeTaskPo.getPkId();
    }

    /**
     * @Method addScriptAnalyzeTask
     * @Param
     * @Param scriptAnalyzeTaskRequestDto
     * @Return com.hex.ds.hdtp.core.inf.script.analyze.po.ScriptAnalyzeTaskPo
     * @Description 新增脚本分析任务
     * @Author gj.xu
     * @Date 2023/12/8 13:52
     * @Version V2.0
     */
    private ScriptAnalyzeTaskPo addScriptAnalyzeTask(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        ScriptAnalyzeTaskPo scriptAnalyzeTaskPo = converter.toScriptAnalyzeTaskPo(scriptAnalyzeTaskRequestDto);
        scriptAnalyzeTaskRepository.add(scriptAnalyzeTaskPo);
        // 获取当前任务脚本目录
        scriptAnalyzeTaskPo.setSourceFilePath(getFileRelativePath(scriptAnalyzeTaskPo.getPkId(), "source-file"));
        scriptAnalyzeTaskRepository.modifyById(scriptAnalyzeTaskPo);
        return scriptAnalyzeTaskPo;
    }

    private void addScriptAnalyzeList(ScriptAnalyzeTaskPo scriptAnalyzeTaskPo, ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        File sourceFile = new File(scriptAnalyzeTaskPo.getSourceFilePath());
        for (String filePath : scriptAnalyzeTaskRequestDto.getFileList()) {
            File uploadFile = new File(filePath);
            FileUtil.copyFilesFromDir(uploadFile, sourceFile, true);
            ScriptAnalyzeListRequestDto scriptAnalyzeList = converter.toScriptAnalyzeListRequestDto(scriptAnalyzeTaskPo, uploadFile);
            scriptAnalyzeList.setSourceFileSize(uploadFile.length() + "");
            scriptAnalyzeListService.add(scriptAnalyzeList);
        }
    }

    /**
     * @Method modifyById
     * @Param
     * @Param scriptAnalyzeTaskRequestDto
     * @Return void
     * @Description 根据主键更新
     * @Author gj.xu
     * @Date 2023/12/8 11:41
     * @Version V2.0
     */
    @Override
    public void modifyById(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        int count = scriptAnalyzeTaskRepository.modifyById(converter.toScriptAnalyzeTaskPo(scriptAnalyzeTaskRequestDto));
        if (count != 1) {
            throw new RuntimeException("更新指定转换任务失败");
        }
    }

    /**
     * @Method modifyTotal
     * @Param
     * @Param pkId
     * @Param total
     * @Return void
     * @Description 更新分析总数
     * @Author gj.xu
     * @Date 2023/12/8 15:52
     * @Version V2.0
     */
    @Override
    public void modifyTotal(String pkId, Integer total) {
        scriptAnalyzeTaskRepository.modifyTotal(pkId, total);
    }

    /**
     * @Method modifySuccessCount
     * @Param
     * @Param pkId
     * @Return void
     * @Description 更新成功数量及状态
     * @Author gj.xu
     * @Date 2023/12/8 15:53
     * @Version V2.0
     */
    @Override
    public void modifySuccessCount(String pkId) {
        // 更新成功数量
        scriptAnalyzeTaskRepository.modifySuccessCount(pkId);
        // 更新任务状态
        scriptAnalyzeTaskRepository.modifyStatus(pkId);
    }

    /**
     * @Method modifyFailCount
     * @Param
     * @Param pkId
     * @Return void
     * @Description 更新失败数量及状态
     * @Author gj.xu
     * @Date 2023/12/8 16:50
     * @Version V2.0
     */
    @Override
    public void modifyFailCount(String pkId) {
        // 更新失败数量
        scriptAnalyzeTaskRepository.modifyFailCount(pkId);
        // 更新任务状态
        scriptAnalyzeTaskRepository.modifyStatus(pkId);
    }

    /**
     * @Method batchRemoveById
     * @Param
     * @Param pkIds
     * @Return void
     * @Description 批量删除脚本分析任务
     * @Author gj.xu
     * @Date 2023/12/8 11:37
     * @Version V2.0
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void batchRemoveById(List<String> pkIds) {
        for (String pkId : pkIds) {
            // 删除任务
            removeById(pkId);
            // 删除对应的脚本清单
            scriptAnalyzeListService.removeByTaskId(pkId);
            // 删除分析任务和各迁移模块任务关系
            scriptAnalyzeTransferService.removeByAnalyzeTaskId(pkId);
        }
    }

    /**
     * @Method removeById
     * @Param
     * @Param pkId
     * @Return void
     * @Description 根据主键删除对应的转换任务
     * @Author gj.xu
     * @Date 2023/12/8 10:14
     * @Version V2.0
     */
    private void removeById(String pkId) {
        if (scriptAnalyzeTaskRepository.removeById(pkId) != 1) {
            throw new RuntimeException("删除分析任务失败");
        }
    }

    /**
     * @Method scriptAnalyze
     * @Param
     * @Param pkId
     * @Return void
     * @Description 开始分析脚本
     * @Author gj.xu
     * @Date 2023/12/8 17:21
     * @Version V2.0
     */
    @Override
    public void scriptAnalyze(String pkId) {
        ScriptAnalyzeTaskResponseDto scriptAnalyzeTaskResponseDto = queryById(pkId);
        if (BasicTaskStatus.TODO.name().equals(scriptAnalyzeTaskResponseDto.getStatus())) {
            scriptAnalyzeTaskRepository.modifyStartTsById(pkId, BasicTaskStatus.PROCESSING.name());
            toScriptAnalyze(pkId);
        }
    }

    /**
     * @Method scriptAnalyzeType
     * @Param
     * @Return java.util.List<java.lang.String>
     * @Description 获取脚本分析插件类型
     * @Author gj.xu
     * @Date 2023/12/11 12:47
     * @Version V2.0
     */
    @Override
    public List<String> scriptAnalyzeType() {
        return scriptAnalyzeService.scriptConvertType();
    }

    /**
     * @Method upload
     * @Param multipartFileList
     * @Return java.util.List<java.io.File>
     * @Description 上传脚本
     * @Author gj.xu
     * @Date 2023/12/11 12:46
     * @Version V2.0
     */
    @Override
    public List<File> upload(List<MultipartFile> multipartFileList) {
        String uploadFilePath = getTmpFilePath("upload-file");
        String sourceFilePath = getTmpFilePath("source-file");
        try {
            for (MultipartFile multipartFile : multipartFileList) {
                File uploadFile = new File(uploadFilePath + File.separator + multipartFile.getOriginalFilename());
                FileUtil.mkdir(uploadFile);
                multipartFile.transferTo(uploadFile);
                if (FileUtil.getType(uploadFile).equalsIgnoreCase("zip")) {
                    ZipUtil.unzip(uploadFile, new File(sourceFilePath), Charset.forName("GBK"));
                } else {
                    FileUtil.copyFilesFromDir(uploadFile, new File(sourceFilePath + File.separator + multipartFile.getOriginalFilename()), true);
                }
            }
        } catch (Exception e) {
            log.error("文件上传失败:{}", e.getMessage(), e);
            throw new RuntimeException("文件上传失败！");
        }
        return FileUtil.loopFiles(sourceFilePath, scriptFileFilter);
    }

    /**
     * @Method createTransferTask
     * @Param scriptAnalyzeTaskRequestDto
     * @Return void
     * @Description 创建各迁移任务
     * @Author gj.xu
     * @Date 2023/12/12 18:27
     * @Version V2.0
     */
    @Override
    public void createTransferTask(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        if (!FINISH.name().equals(scriptAnalyzeTaskRepository.queryById(scriptAnalyzeTaskRequestDto.getPkId()).getStatus())) {
            throw new RuntimeException("当前任务脚本分析还未完成，请稍等");
        }
        // 添加表迁移任务相关
        addTableTransferTask(scriptAnalyzeTaskRequestDto);
        // 添加数据迁移任务相关
        addDataTransferTask(scriptAnalyzeTaskRequestDto);
        // 添加脚本迁移任务相关
        addScriptTransferTask(scriptAnalyzeTaskRequestDto);
    }

    /**
     * @Method addTableTransferTask
     * @Param scriptAnalyzeTaskRequestDto
     * @Return void
     * @Description 添加表迁移任务相关
     * @Author gj.xu
     * @Date 2023/12/13 15:19
     * @Version V2.0
     */
    public void addTableTransferTask(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        if (!scriptAnalyzeTaskRequestDto.getTransferTaskList().contains(TABLE_TRANSFER.name())) {
            return;
        }
        String analyzeTaskPkId = scriptAnalyzeTaskRequestDto.getPkId();
        String sourceConnPkId = scriptAnalyzeTaskRequestDto.getSourceConnPkId();
        String targetConnPkId = scriptAnalyzeTaskRequestDto.getTargetConnPkId();
        // 添加表迁移任务
        TableTransferTaskRequestDto tableTransferTaskRequestDto = new TableTransferTaskRequestDto();
        tableTransferTaskRequestDto.setSourceConnPkId(sourceConnPkId);
        tableTransferTaskRequestDto.setTargetConnPkId(targetConnPkId);
        tableTransferTaskRequestDto.setTaskName("SCRIPT_ANALYZE_" + scriptAnalyzeTaskRequestDto.getTaskName());
        tableTransferTaskRequestDto.setTransferMethod("1");
        String tableTransferTaskId = tableTransferTaskService.addWithBackId(tableTransferTaskRequestDto);
        // 添加表迁移清单各表相关
        addTableTransferList(analyzeTaskPkId, tableTransferTaskId, sourceConnPkId, targetConnPkId);
        // 添加脚本分析任务和表迁移任务关系
        scriptAnalyzeTransferService.add(analyzeTaskPkId, tableTransferTaskId, TABLE_TRANSFER.name());

    }

    /**
     * @Method addTableTransferList
     * @Param analyzeTaskPkId
     * @Param sourceConnPkId
     * @Param targetConnPkId
     * @Return void
     * @Description 添加表迁移清单各表相关
     * @Author gj.xu
     * @Date 2023/12/13 15:32
     * @Version V2.0
     */
    private void addTableTransferList(String analyzeTaskPkId, String tableTransferTaskId, String sourceConnPkId, String targetConnPkId) {
        List<ScriptTableConsanguinityResponseDto> tables = scriptTableConsanguinityService.queryListByTaskId(analyzeTaskPkId);
        for (ScriptTableConsanguinityResponseDto table : tables) {
            String dbName = "HDTP";
            String tableName;
            String[] parts = table.getTableName().split("\\.");
            if (parts.length == 2) {
                dbName = parts[0];
                tableName = parts[1];
            } else {
                tableName = parts[0];
            }
            // 创建表迁移清单各表
            TableTransferListRequestDto tableTransferListRequestDto = new TableTransferListRequestDto();
            tableTransferListRequestDto.setTaskPkId(tableTransferTaskId);
            tableTransferListRequestDto.setSourceConnPkId(sourceConnPkId);
            tableTransferListRequestDto.setTargetConnPkId(targetConnPkId);
            tableTransferListRequestDto.setPartitionFieldName("DT");
            tableTransferListRequestDto.setSourceDbName(dbName.toUpperCase());
            tableTransferListRequestDto.setSourceTableName(tableName.toUpperCase());
            tableTransferListRequestDto.setStatus("UNCREATE");
            tableTransferListRequestDto.setTransferMethod("1");
            String tableTransferListId = tableTransferListService.addWithBackId(tableTransferListRequestDto);
            // 创建表迁移清单各表字段映射
            tableTransferListService.getFieldMap(sourceConnPkId, targetConnPkId, tableTransferListId);
            // 创建表迁移清单各表建表语句
            tableTransferListService.buildCreateTableSql(tableTransferListId);
        }
    }

    public void addDataTransferTask(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        // 添加数据迁移任务需配置创建表迁移任务
        if (!scriptAnalyzeTaskRequestDto.getTransferTaskList().contains(TABLE_TRANSFER.name())
                || !scriptAnalyzeTaskRequestDto.getTransferTaskList().contains(DATA_TRANSFER.name())) {
            return;
        }

    }

    public void addScriptTransferTask(ScriptAnalyzeTaskRequestDto scriptAnalyzeTaskRequestDto) {
        if (!scriptAnalyzeTaskRequestDto.getTransferTaskList().contains(SCRIPT_TRANSFER.name())) {
            return;
        }

    }

    /**
     * @Method getTmpFilePath
     * @Param
     * @Param fileType
     * @Return java.lang.String
     * @Description 获取临时文件目录
     * @Author gj.xu
     * @Date 2023/12/11 11:53
     * @Version V2.0
     */
    private String getTmpFilePath(String fileType) {
        String filePath = StrUtil.format("{}/tmp/{}/{}/{}", relativePath, DateUtil.today(), fileType, System.currentTimeMillis());
        FileUtil.mkdir(filePath);
        return filePath;
    }

    /**
     * @Method toScriptAnalyze
     * @Param
     * @Param convertTaskPkId
     * @Return void
     * @Description
     * @Author gj.xu
     * @Date 2023/12/8 17:21
     * @Version V2.0
     */
    public void toScriptAnalyze(String pkId) {
        ScriptAnalyzeTaskPo scriptAnalyzeTaskPo = scriptAnalyzeTaskRepository.queryById(pkId);
        List<ScriptAnalyzeListResponseDto> scriptAnalyzeList = scriptAnalyzeListService.queryByTaskId(pkId);
        for (ScriptAnalyzeListResponseDto scriptAnalyzeListRequestDto : scriptAnalyzeList) {
            scriptAnalyzeService.scriptAnalyzeHandler(scriptAnalyzeListRequestDto, scriptAnalyzeTaskPo);
        }
    }

    /**
     * @Method getFileRelativePath
     * @Param
     * @Param taskId
     * @Param fileType
     * @Return java.lang.String
     * @Description 获取该任务脚本文件完整目录
     * @Author gj.xu
     * @Date 2023/12/8 13:45
     * @Version V2.0
     */
    private String getFileRelativePath(String taskId, String fileType) {
        String filePath = StrUtil.format("{}/{}/{}/{}", relativePath, DateUtil.today(), taskId, fileType);
        FileUtil.mkdir(filePath);
        return filePath;
    }
}
